package com.xishu.service.order.orderItem;

import com.xishu.aop.Validator;
import com.xishu.bo.Stock;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.config.ContextConstant;
import com.xishu.entity.User;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.Order;
import com.xishu.entity.order.OrderItem;
import com.xishu.entity.order.SendPrinter;
import com.xishu.entity.shop.*;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.service.order.base.OrderService;
import com.xishu.util.ClassUtil;
import com.xishu.util.ServletUtil;
import com.xishu.util.Sha256Util;
import com.xishu.util.Tools;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.*;
import static com.xishu.response.ResponseStatus.ERROR_USER;

public class OrderItemChain extends ChainBase implements Tools {
    private static Logger logger = LoggerFactory.getLogger(OrderItemChain.class);
    private OrderService orderService = OrderService.getInstance();

    public OrderItemChain(Order reqOrder) {
        addCommand(new VerifyCmd());

        if (orderService.isUpdateOrDeleteAction(reqOrder)) {
            OrderItem reqOrderItem = reqOrder.getOrderItemDetailList().get(0);
            if (getBoolean(reqOrderItem.getDelete())) {
                addCommand(new DeleteOrderItemCmd());
            } else if (reqOrderItem.getUpdatePrice() != null) {
                addCommand(new UpdatePriceCmd());
            } else if (reqOrderItem.getCount() != null) {
                addCommand(new UpdateCountCmd());
            } else {
                addCommand(new UpdateOrderItemCmd());
            }

            addCommand(new UpdateOrDeleteResultCmd());
        } else {
            addCommand(new VerifyAddOrderItemCmd());
            addCommand(new AddSpecialItemCmd());
            addCommand(new AddNewOrderItemCmd());
            addCommand(new AddSameOrderItemCmd());
        }
    }

    public OrderItemChain() {
    }

    public ResponseData orderItemAction(Order reqOrder) throws Exception {
        OrderItemChain orderItemChain = new OrderItemChain(reqOrder);
        Context context = new ContextBase();
        context.put(ContextConstant.CONTEXT_ORDER, reqOrder);
        OrderService orderService = OrderService.getInstance();
        LockService localLockService = LockService.getInstance();

        logger.info("will lock the order {}", reqOrder.getId());
        String lockKey = orderService.getLockKey(reqOrder);
        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            orderItemChain.execute(context);
            return (ResponseData) context.get(ContextConstant.CONTEXT_RESULT);
        }
    }


}

class VerifyCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(VerifyCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        //必须要有订单号
        VerifyUtil.verify(() -> isNotEmpty(reqOrder.getId()));
        VerifyUtil.verify(() -> isNotEmpty(reqOrder.getShopId()));
        //一次添加一个
        VerifyUtil.verify(() -> isNotEmpty(reqOrder.getOrderItemDetailList()));

        Shop shop = ShopService.getInstance().findShopById(reqOrder.getShopId());
        Long shopId = reqOrder.getShopId();
        Long orderId = reqOrder.getId();
        logger.info("check shop status");
        VerifyUtil.verify(() -> getBoolean(shop.getBusinessStatus()), ResponseStatus.SHOP_CLOSED);

        context.put(ContextConstant.SHOP, shop);
        context.put(ContextConstant.SHOP_ID, shopId);
        context.put(ContextConstant.ORDER_ID, orderId);

        Order orderInDb = orderService.findOrder(shopId, orderId, false);
        VerifyUtil.verify(() -> !getBoolean(orderInDb.getExpiredOrder()), ResponseStatus.ORDER_EXPIRED);

        orderService.checkPayingOrder(orderInDb);
        context.put(ContextConstant.ORDER_IN_DB, orderInDb);
        OrderItem reqOrderItem = getList(reqOrder.getOrderItemDetailList()).get(0);
        context.put(ContextConstant.ORDER_ITEM, getList(reqOrder.getOrderItemDetailList()).get(0));

        if (orderService.isUpdateOrDeleteAction(reqOrder)) {
            checkConfirmOrderItem(orderInDb, reqOrderItem);
        }

        return false;
    }

    /**
     * 已经落单的菜不能再次被修改了
     *
     * @param orderInDb
     * @param reqOrderItem
     * @throws Exception
     */
    private void checkConfirmOrderItem(Order orderInDb, OrderItem reqOrderItem) throws Exception {
        User user = ServletUtil.getUser();

        if (user == null) {
            //已经落单的菜，在C端不能修改
            List<OrderItem> orderItemDetailListInDb = getList(orderInDb.getOrderItemDetailList());
            Optional<OrderItem> orderItemOptional = orderItemDetailListInDb.stream().filter(p -> equals(p.getId(), reqOrderItem.getId())).findFirst();

            if (orderItemOptional.isPresent()) {
                VerifyUtil.verify(() -> !getBoolean(orderItemOptional.get().getCheckout()), ResponseStatus.ORDER_ITEM_CONFIRMED);
            }
        }
    }
}

class DeleteOrderItemCmd implements Command, Tools {
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private static Logger logger = LoggerFactory.getLogger(DeleteOrderItemCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        logger.info("execute DeleteOrderItemCmd");
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!orderService.isUpdateOrDeleteAction(reqOrder)) {
            logger.info("not delete or update action");
            return false;
        }

        if (!getBoolean(reqOrderItem.getDelete())) {
            logger.info("not the delete item action");
            return false;
        }

        List<Long> deleteItemIdList = getList(reqOrder.getOrderItemDetailList()).stream().map(p -> p.getId()).collect(Collectors.toList());

        //直接删除锁定量
        StockService.getInstance().releaseOrderItemLockCount(orderInDb, deleteItemIdList);

        //添加删除数据到报表
        for (Long orderItemId : deleteItemIdList) {
            logger.info("will add delete order item {}", orderItemId);
            ReportService.getInstance().addDeleteOrderItem(orderItemId, orderInDb);
        }

        //做检查
        List<OrderItem> deleteOrderItemList = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> deleteItemIdList.contains(p.getId())).collect(Collectors.toList());
        List<SendPrinter> sendPrinterList = getList(orderInDb.getSendPrinterList());
        orderService.checkDeleteItem(deleteOrderItemList, sendPrinterList, reqOrder.getCustomerRequest());
        //end 做检查

        //批量过滤,只保存不删除的对象
        List<OrderItem> leftItemList = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> !deleteItemIdList.contains(p.getId().longValue())).collect(Collectors.toList());
        //日志需要，保留删除的对象
        List<OrderItem> LogLeftItemList = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> deleteItemIdList.contains(p.getId().longValue())).collect(Collectors.toList());

        orderInDb.setOrderItemDetailList(leftItemList);

        //过滤需要删除订单锁定菜中无用的orderItem
        List<OrderItemLockNumber> itemLockNumberList = getList(orderInDb.getItemLockNumberList()).stream().filter(p -> !deleteItemIdList.contains(p.getOrderItemId().longValue())).collect(Collectors.toList());
        orderInDb.setItemLockNumberList(itemLockNumberList);
        orderInDb.setSendPrinterList(getList(orderInDb.getSendPrinterList()).stream().filter(p -> p.getOrderItemId().longValue() != reqOrderItem.getId().longValue()).collect(Collectors.toList()));

        //如果删除之后，发送列表为空了，同步更新餐桌的已发送列表
        if (isEmpty(orderInDb.getSendPrinterList())) {
            List<FoodTable> foodTableList = FoodTableService.getInstance().findFoodTableListByOrder(orderInDb);
            for (FoodTable foodTable : foodTableList) {
                logger.info("set food table {} send to false", foodTable.getId());
                foodTable.setSendPrinter(false);
                commonService.save(foodTable);
            }
        }

        logger.info("will cal price");
        //只要还能够添加，那么清空优惠券
        orderInDb.setTicketCodeList(new ArrayList<>());
        orderInDb.setParentTicketCodeList(new ArrayList<>());

        orderService.calPriceForB_C_END(orderInDb);
        commonService.save(orderInDb);

        //删除orderItem写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog OrderOpLogContent = orderOpLogService.content(orderInDb, SUB_ITEM);
        orderOpLogService.subOrderItemLog(LogLeftItemList, OrderOpLogContent);


        //服务员操作的时候,删除菜品之后，处理餐桌的状态
        if (ServletUtil.getUserId() != null) {
            FoodTableService.getInstance().dealOrderFoodTableStatus(orderInDb);

            //只显示checkout的菜品
            List<OrderItem> orderItemList = orderInDb.getOrderItemDetailList().stream().filter(p -> getBoolean(p.getCheckout()) && getBoolean(p.getConfirmItem())).collect(Collectors.toList());
            orderInDb.setOrderItemDetailList(orderItemList);
        }

        context.put(ContextConstant.STOCK_LIST, new ArrayList<>());

        return false;
    }
}

class UpdateCountCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(UpdateCountCmd.class);
    private OrderService orderService = OrderService.getInstance();

    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        logger.info("execute UpdateCountCmd");
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!orderService.isUpdateOrDeleteAction(reqOrder)) {
            logger.info("not delete or update action");
            return false;
        }

        if (!(reqOrderItem.getCount() != null)) {
            logger.info("not update order item count");
            return false;
        }

        //这里的count一定只是1，或者-1，因为界面上只有一个加减的操作
        Optional<OrderItem> orderItemOptional = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> p.getId().longValue() == reqOrderItem.getId().longValue()).findFirst();
        OrderItem orderItem = orderItemOptional.get();
        int countAfterUpdate = getOrderItemCount(reqOrderItem, orderItem);
        OrderItem orderItemClone = (OrderItem) ClassUtil.clone(orderItem);
        //如果是B端的添加的菜，那么需要处理锁定量，如果是C端先付款模式下，需要处理锁定量，如果是C端后付款模式，由于已经发送到厨房了，不用处理锁定量
        List<Stock> stockList = StockService.getInstance().waiterUpdateOrderItemLock(orderInDb, orderItem, reqOrderItem.getCount() > 0, countAfterUpdate);

        if (isNotEmpty(stockList)) {
            logger.info("some lock item not available");
            context.put(ContextConstant.STOCK_LIST, stockList);
            return false;
        }

        //添加一个被删除的菜
        if (reqOrderItem.getCount() < 0) {
            ReportService.getInstance().cutOneOrderItem(reqOrderItem.getId(), orderInDb);
        }

        //做检查
        List<OrderItem> modifyOrderItemList = new ArrayList<>();
        modifyOrderItemList.add(orderItem);
        List<SendPrinter> sendPrinterList = getList(orderInDb.getSendPrinterList());
        orderService.checkDeleteItem(modifyOrderItemList, sendPrinterList, reqOrder.getCustomerRequest());
        //end 做检查
        orderItem.setCount(countAfterUpdate);

        //如果数量已经小于等于0，则把订单里面的OrderItem删除掉
        if (orderItem.getCount() <= 0) {
            orderInDb.setOrderItemDetailList(orderInDb.getOrderItemDetailList().stream().filter(orderItem_tmp -> !equals(orderItem_tmp.getId(), reqOrderItem.getId())).collect(Collectors.toList()));
        }

        logger.info("will cal price");
        //只要还能够添加，那么清空优惠券
        orderInDb.setTicketCodeList(new ArrayList<>());
        orderInDb.setParentTicketCodeList(new ArrayList<>());
        orderService.calPriceForB_C_END(orderInDb);
        orderInDb.setUnassignCash(orderInDb.getUserCost());
        commonService.save(orderInDb);
        //减菜数量写入日志

        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog;
        //减菜操作
        if (reqOrderItem.getCount() < 0) {
            orderOpLog = orderOpLogService.content(orderInDb, SUB_ITEM);
            orderOpLogService.subOrderItemCountLog(orderItemClone, orderOpLog);
        } else {
            //加菜
            orderOpLog = orderOpLogService.content(orderInDb, ADD_ITEM);
            orderOpLogService.addOrderItemCountLog(orderItemClone, orderOpLog);
        }


        context.put(ContextConstant.STOCK_LIST, new ArrayList<>());

        return false;
    }

    private int getOrderItemCount(OrderItem reqOrderItem, OrderItem orderItem) {
        //B端只能一个一个的加
        int count = getInt(orderItem.getCount()) + reqOrderItem.getCount();


        if (count < 0) {
            count = 0;
        }
        return count;
    }
}

class UpdatePriceCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(UpdatePriceCmd.class);
    private OrderService orderService = OrderService.getInstance();

    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        logger.info("execute UpdatePriceCmd");
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!orderService.isUpdateOrDeleteAction(reqOrder)) {
            logger.info("not delete or update action");
            return false;
        }

        if (!(reqOrderItem.getUpdatePrice() != null)) {
            logger.info("not update order item count");
            return false;
        }

        Long userId = ServletUtil.getUserId();
        VerifyUtil.verify(() -> userId != null);

        VerifyUtil.verify(() -> !orderService.isCustomerRequest(reqOrder));
        VerifyUtil.verify(() -> reqOrderItem.getItemPrice() != null);

        Optional<OrderItem> orderItemOptional = orderInDb.getOrderItemDetailList().stream().filter(orderItem -> equals(orderItem.getId(), reqOrderItem.getId())).findAny();
        VerifyUtil.verify(() -> orderItemOptional.isPresent());
        OrderItem orderItemInDb = orderItemOptional.get();

        List<OrderItem> orderItemList = orderInDb.getOrderItemDetailList().stream().filter(orderItem -> !equals(orderItem.getId(), reqOrderItem.getId())).collect(Collectors.toList());
        reqOrderItem.setCount(orderItemInDb.getCount());
        reqOrderItem.setCheckout(true);
        reqOrderItem.setConfirmItem(true);
        orderItemList.add(reqOrderItem);
        orderInDb.setOrderItemDetailList(orderItemList);

        //重新计算价格
        orderService.calPriceForB_C_END(orderInDb);
        commonService.save(orderInDb);

        context.put(ContextConstant.STOCK_LIST, new ArrayList<>());

        return false;
    }
}

/**
 * 更新菜的内容
 */
class UpdateOrderItemCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(UpdateOrderItemCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private ItemService itemService = ItemService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        logger.info("execute UpdateOrderItemCmd");
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!orderService.isUpdateOrDeleteAction(reqOrder)) {
            logger.info("not delete or update action");
            return false;
        }
        Order cloneOrderInDb = (Order) ClassUtil.clone(orderInDb);
        //修改
        //如果是C端修改，需要先查看是否有一样的项，如果有一样的，则直接修改数量
        OrderItem sameOtherOrderItem = orderService.findSameOrderItem(reqOrderItem, orderInDb, ServletUtil.isCustomerRequest());
        //没有相同的项，则直接修改，修改的时候需要重新计算价格
        //sameOrderItem的含义是指orderItemId相同，规格细节也是相同的
        if (sameOtherOrderItem == null) {
            //重要，这里是修改
            logger.info("will modify the order item");
            Optional<OrderItem> orderItemOptional = getList(orderInDb.getOrderItemDetailList()).stream().filter(p -> p.getId().longValue() == reqOrderItem.getId().longValue()).findFirst();
            VerifyUtil.verify(() -> orderItemOptional.isPresent(), ResponseStatus.ORDER_ITEM_DELETE);

            OrderItem orderItem = orderItemOptional.get();
            reqOrderItem.setItemId(orderItem.getItemId());
            reqOrderItem.setItemImgUrl(orderItem.getItemImgUrl());
            reqOrderItem.setItemName(orderItem.getItemName());
            reqOrderItem.setItemName_zh(orderItem.getItemName_zh());
            reqOrderItem.setItemName_en(orderItem.getItemName_en());
            reqOrderItem.setMainItemId(orderItem.getMainItemId());
            reqOrderItem.setSceneId(orderItemOptional.get().getSceneId());

            if (reqOrderItem.getCount() == null) {
                reqOrderItem.setCount(orderItem.getCount());
            }

            //新添加的项，需要做国际化处理
            //时价菜不用再处理价格
            Long itemId = orderItem.getItemId();
            Item item = ItemService.getInstance().findItem(itemId);

            if (!getBoolean(item.getRealPriceItem())) {
                OrderItem priceOrderItem = orderService.calItemPrice(reqOrderItem, orderInDb);
                reqOrderItem.setItemPrice(priceOrderItem.getItemPrice());
                reqOrderItem.setItemTotalCost(priceOrderItem.getItemTotalCost());
                reqOrderItem.setBasePrice(priceOrderItem.getBasePrice());
            }

            //判断库存
            List<Stock> stocks = StockService.getInstance().updateOrderItem(orderInDb, reqOrderItem);
            if (isNotEmpty(stocks)) {
                context.put(ContextConstant.STOCK_LIST, stocks);
                return false;
            }

            BeanUtils.copyProperties(reqOrderItem, orderItem);
            logger.info("will cal price");
            if (!equals(orderItem.getItemId(), SPECIAL_ITEM)) {
                orderService.orderItemZhEn(orderItem, itemService.findItem(orderItem.getItemId()));
            }


            OrderOpLogService opLogService = OrderOpLogService.getInstance();

            if (!getBoolean(reqOrder.getCustomerRequest())) {
                //更新B端购物车
                OrderOpLogService opLogService_B = OrderOpLogService.getInstance();
                OrderOpLog foodTableContent_B = opLogService_B.content(orderInDb, Constant.UPDATE_ORDER_ITEM);
                opLogService_B.updateOrderItem_B(reqOrderItem, cloneOrderInDb, ServletUtil.isCustomerRequest(), foodTableContent_B);
            }else {
                //更新了C端改变后的购物车
                OrderOpLog foodTableContent_C = opLogService.content(orderInDb, Constant.UPDATE_ORDER_ITEM);
                List<OrderItem> orderItems = new ArrayList<>();
                orderItems.add(reqOrderItem);
                opLogService.updateOrderItemChange_C(orderItems, foodTableContent_C);

            }

            //只要还能够添加，那么清空优惠券
            orderInDb.setTicketCodeList(new ArrayList<>());
            orderInDb.setParentTicketCodeList(new ArrayList<>());
            orderService.calPriceForB_C_END(orderInDb);
            commonService.save(orderInDb);
            context.put(ContextConstant.STOCK_LIST, new ArrayList<>());
            return false;
        } else if (equals(sameOtherOrderItem.getId(), reqOrderItem.getId())) {
            //更新了C端 这里orderItem没有任何改变
            OrderOpLogService opLogService = OrderOpLogService.getInstance();
            OrderOpLog foodTableContent = opLogService.content(orderInDb, Constant.UPDATE_ORDER_ITEM);
            opLogService.updateOrderItem_C(reqOrderItem, foodTableContent);

            logger.info("user do not modify any item");
            return false;
        } else {
            //有相同的项，添加数量
            //C端场景，有相同项，新增数量
            logger.info("find the same order item {}", sameOtherOrderItem);
            OrderItem orderItem = orderService.filterOrderItemList(orderInDb, reqOrderItem.getId());
            reqOrderItem.setCount(orderItem.getCount());

            Integer addCount = getInt(reqOrderItem.getCount());
            sameOtherOrderItem.setCount(getInt(sameOtherOrderItem.getCount()) + addCount);

            //删除掉当前的这个菜
            List<OrderItem> orderItemDetailList = getList(orderInDb.getOrderItemDetailList());
            List<OrderItem> orderItemList = orderItemDetailList.stream().filter(p -> !equals(p.getId(), reqOrderItem.getId())).collect(Collectors.toList());
            orderInDb.setOrderItemDetailList(orderItemList);

            //打印日志
            logger.info("order item list is {}, and will cal price", StringUtils.join(orderItemList, ","));

            //只要还能够添加，那么清空优惠券
            orderInDb.setTicketCodeList(new ArrayList<>());
            orderInDb.setParentTicketCodeList(new ArrayList<>());
            orderService.calPriceForB_C_END(orderInDb);
            commonService.save(orderInDb);
            context.put(ContextConstant.STOCK_LIST, new ArrayList<>());
            return false;
        }
    }
}

/**
 * 更新或者删除结果汇总
 */
class UpdateOrDeleteResultCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(UpdateOrDeleteResultCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!orderService.isUpdateOrDeleteAction(reqOrder)) {
            logger.info("not delete or update action");
            return false;
        }

        List<Stock> stockList = (List<Stock>) context.get(ContextConstant.STOCK_LIST);

        if (isNotEmpty(stockList)) {
            logger.info("stock not available");
            ResponseData responseData = commonService.createResponse(stockList);
            context.put(ContextConstant.CONTEXT_RESULT, responseData);
            return true;
        }

        logger.info("stock available");

        //最近返回的时候，判断是否需要过滤
        if (!orderService.isCustomerRequest(reqOrder)) {
            orderService.filterCheckoutOrderItem(orderInDb);
        }

        orderService.showPrice(orderInDb, null);
        ResponseData responseData = commonService.createResponse(orderInDb);
        context.put(ContextConstant.CONTEXT_RESULT, responseData);

        return true;
    }
}

/**
 * 加菜时候的验证
 */
class VerifyAddOrderItemCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(VerifyAddOrderItemCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        Long userId = ServletUtil.getUserId();

        if (userId == null && equals(reqOrderItem.getItemId(), SPECIAL_ITEM)) {
            logger.info("not login user but add special item");
            //如果是特殊的菜，却没有登录
            VerifyUtil.throwError(ERROR_USER);
        }

        return false;
    }
}

class AddSpecialItemCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddSpecialItemCmd.class);
    private CommonService commonService = CommonService.getInstance();
    private OrderService orderService = OrderService.getInstance();


    @Override
    public boolean execute(Context context) throws Exception {
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        if (!(ServletUtil.getUserId() != null && equals(reqOrderItem.getItemId(), SPECIAL_ITEM))) {
            logger.info("not special order item");
            return false;
        }

        //默认均可以发送到厨房
        reqOrderItem.setCanSendToKitchen(true);

        //价格一定要传
        VerifyUtil.verify(() -> reqOrderItem.getItemPrice() != null);

        //处理国际化
        if (isNotEmpty(reqOrderItem.getItemName()) || isNotEmpty(reqOrderItem.getItemName_zh()) || isNotEmpty(reqOrderItem.getItemName_en())) {
            ZhEnService.getInstance().dealZhEn(reqOrderItem);
        } else {
            //默认名称
            reqOrderItem.setItemName_zh(SPECIAL_ITEM_NAME_ZH);
            reqOrderItem.setItemName_en(SPECIAL_ITEM_NAME_EN);
        }

        Long orderItemId = orderService.generateOrderItemId(orderInDb.getShopId());
        reqOrderItem.setId(orderItemId);
        reqOrderItem.setCustomerAdd(false);
        reqOrderItem.setCheckout(true);
        reqOrderItem.setConfirmItem(true);
        orderInDb.setBusinessEndShow(true);
        //一次只能添加一个
        reqOrderItem.setCount(1);

        if (isEmpty(orderInDb.getOrderItemDetailList())) {
            orderInDb.setOrderItemDetailList(new ArrayList<>());
        }

        orderInDb.getOrderItemDetailList().add(reqOrderItem);

        //只要还能够添加，那么清空优惠券
        orderInDb.setTicketCodeList(new ArrayList<>());
        orderInDb.setParentTicketCodeList(new ArrayList<>());
        orderService.calPriceForB_C_END(orderInDb);
        commonService.save(orderInDb);

        //添加特别菜，写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, ADD_ITEM);
        orderOpLogService.addSpecialItemLog(reqOrder, orderOpLog);
        //最近返回的时候，判断是否需要过滤
        orderService.filterCheckoutOrderItem(orderInDb);

        //服务员操作的时候,添加处理餐桌状态
        if (getList(orderInDb.getOrderItemDetailList()).size() == 1) {
            logger.info("first add to order {}, deal food table status ", orderInDb.getId());
            FoodTableService.getInstance().dealOrderFoodTableStatus(orderInDb);
        }

        //只显示checkout的菜品
        List<OrderItem> orderItemList = orderInDb.getOrderItemDetailList().stream().filter(p -> getBoolean(p.getCheckout())).collect(Collectors.toList());

        //如果是服务员端，显示服务员确认的
        //如果是C端，只要落单的都要显示
        if (ServletUtil.getUserId() != null) {
            orderItemList = orderItemList.stream().filter(p -> getBoolean(p.getConfirmItem())).collect(Collectors.toList());
        }

        orderInDb.setOrderItemDetailList(orderItemList);

        orderService.showPrice(orderInDb, null);

        ResponseData responseData = commonService.createResponse(orderInDb);
        context.put(ContextConstant.CONTEXT_RESULT, responseData);

        return true;
    }
}

/**
 * 添加新菜到订单
 */
class AddNewOrderItemCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddSpecialItemCmd.class);
    private OrderService orderService = OrderService.getInstance();
    private CommonService commonService = CommonService.getInstance();
    private ItemService itemService = ItemService.getInstance();
    private Config config = Config.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);

        //开始新增菜
        boolean customerRequest = orderService.isCustomerRequest(reqOrder);

        OrderItem sameOrderItem = orderService.findSameOrderItem(reqOrderItem, orderInDb, reqOrder.getCustomerRequest());
        //没有相同项（包含B端），新增的情况
        //B端一定是新增
        if (sameOrderItem != null) {
            logger.info("find the same order item");
            context.put(ContextConstant.SAME_ORDER_ITEM, sameOrderItem);
            return false;
        }

        //重复的请求，将当前的数据库里面的订单返回
        if (duplicateRequest(reqOrderItem)) {
            context.put(ContextConstant.DUPLICATE_ORDER_ITEM, true);
            context.put(ContextConstant.CONTEXT_RESULT, commonService.createResponse(orderInDb));
            return false;
        }

        //默认均可以发送到厨房
        reqOrderItem.setCanSendToKitchen(true);

        //新增菜品
        logger.info("customerRequest {}", customerRequest);

        List<OrderItem> orderItemDetailList = getList(reqOrder.getOrderItemDetailList());
        VerifyUtil.verify(() -> isNotEmpty(orderItemDetailList));
        Long orderItemId = orderService.generateOrderItemId(orderInDb.getShopId());
        reqOrderItem.setId(orderItemId);
        List<Stock> stockList = StockService.getInstance().waiterAddOneItem(orderInDb, reqOrderItem);

        if (isNotEmpty(stockList)) {
            logger.info("some item not available");
            context.put(ContextConstant.CONTEXT_RESULT, commonService.createResponse(stockList));

            return true;
        }

        //B端在加菜，那么就B端可见
        if (!customerRequest) {
            orderInDb.setBusinessEndShow(true);
        }

        logger.info("first add the order item {}", reqOrderItem.getItemId());

        reqOrderItem.setCustomerAdd(customerRequest);
        //B端的请求，一定会锁定菜
        reqOrderItem.setLockItem(!customerRequest);
        //一次只能添加一个
        reqOrderItem.setCount(1);

        Long itemId = reqOrderItem.getItemId();
        Item item = ItemService.getInstance().findItem(itemId);

        if (!getBoolean(item.getRealPriceItem())) {
            OrderItem priceOrderItem = orderService.calItemPrice(reqOrderItem, orderInDb);
            reqOrderItem.setItemPrice(priceOrderItem.getItemPrice());
            reqOrderItem.setItemTotalCost(priceOrderItem.getItemTotalCost());
            reqOrderItem.setBasePrice(priceOrderItem.getBasePrice());
        } else {
            reqOrderItem.setBasePrice(reqOrderItem.getItemPrice());
            reqOrderItem.setItemTotalCost(reqOrderItem.getItemPrice());
        }

        //添加菜品的基本信息
        reqOrderItem.setExcludeTakeOutDiscount(item.getExcludeTakeOutDiscount());
        reqOrderItem.setExcludeServiceFee(item.getExcludeServiceFee());
        reqOrderItem.setExcludeDiscountTicket(item.getExcludeDiscountTicket());

        //添加品类
        Long catalogId = item.getCatalogId();
        Catalog catalog = CatalogService.getInstance().findCatalogById(catalogId);
        reqOrderItem.setCatalogName_zh(catalog.getName_zh());
        reqOrderItem.setCatalogName_en(catalog.getName_en());
        reqOrderItem.setCatalogId(catalogId);

        //如果是B端添加的，直接就是已确认
        reqOrderItem.setCheckout(!customerRequest);
        reqOrderItem.setConfirmItem(!customerRequest);
        //如果没有场景ID，那么添加场景ID
        if (getLong(reqOrderItem.getSceneId()) == 0) {
            Optional<Scene> sceneOptional = SceneService.getInstance().findSceneOptional(orderInDb.getShopId());

            if (sceneOptional.isPresent()) {
                reqOrderItem.setSceneId(sceneOptional.get().getId());
            } else {
                logger.info("can not find the scene id for shop {}", orderInDb.getShopId());
            }
        }

        Item itemInDb = itemService.findItem(reqOrderItem.getItemId());
        reqOrderItem.setItemName(itemInDb.getName());
        reqOrderItem.setPrintLabel(itemInDb.getPrintLabel());

        //添加主菜ID
        reqOrderItem.setMainItemId(itemInDb.getMainItemId());

        //如果是套餐菜品，添加套餐里面的字段描述
        if (getBoolean(itemInDb.getSetMenu())) {
            logger.info("add set menu item");
            reqOrderItem.setSetMenu(true);
        } else {
            reqOrderItem.setSetMenu(false);
        }

        String endpoint = config.getProductBucketEndpoint();
        reqOrderItem.setItemImgUrl(String.format("https://%s/%s", endpoint, itemInDb.getImgId()));
        orderService.orderItemZhEn(reqOrderItem, itemInDb);

        if (isEmpty(orderInDb.getOrderItemDetailList())) {
            orderInDb.setOrderItemDetailList(new ArrayList<>());
        }

        orderInDb.getOrderItemDetailList().add(reqOrderItem);

        //如果是第一添加，那么判断是否是有账号登录，如果是有账号登录，那么是B端的订单，如果没有账号登录，那么一定是C端的订单
        if (orderInDb.getCustomerOrder() == null) {
            orderInDb.setCustomerOrder(customerRequest);
        }

        //只要还能够添加，那么清空优惠券
        orderInDb.setTicketCodeList(new ArrayList<>());
        orderInDb.setParentTicketCodeList(new ArrayList<>());
        orderService.calPriceForB_C_END(orderInDb);
        commonService.save(orderInDb);
        //添加orderItem写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, ADD_ITEM);
        orderOpLogService.addOrderItemLog(reqOrderItem, orderOpLog);

        //最近返回的时候，判断是否需要过滤
        if (!getBoolean(customerRequest)) {
            orderService.filterCheckoutOrderItem(orderInDb);
        }

        context.put(ContextConstant.CONTEXT_RESULT, commonService.createResponse(orderInDb));

        //B端的请求才处理餐厅状态
        if (ServletUtil.isBEndPayOrder() && orderService.isFoodTableOrder(orderInDb)) {
            FoodTableService.getInstance().dealOrderFoodTableStatus(orderInDb);
        }

        return true;
    }

    /**
     * 是否需要检查相同的请求
     *
     * @param orderItem
     * @return
     */
    private boolean needCheckDuplicateRequest(OrderItem orderItem) throws Exception {
        Long itemId = orderItem.getItemId();
        Item item = itemService.findItem(itemId);
        return ItemService.isCommonItemHasDetail(item) || getBoolean(item.getSetMenu());
    }

    /**
     * 重复请求处理
     *
     * @param orderItem
     * @return
     */
    private boolean duplicateRequest(OrderItem orderItem) throws Exception {
        boolean needCheckDuplicateRequest = needCheckDuplicateRequest(orderItem);

        //不需要检查，肯定也不是重复的请求了
        if (!needCheckDuplicateRequest) {
            return false;
        }

        String sessionId = ServletUtil.getSessionId();

        String key = Sha256Util.sha256(sessionId + ":" + orderItem.toString());
        boolean keyExpire = new Validator().keyExpire(key, config.addToCartDuplicateRequestTime());
        //如果KEY还存在，则算作是重复的请求
        return !keyExpire;
    }
}

class AddSameOrderItemCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(AddSameOrderItemCmd.class);
    private CommonService commonService = CommonService.getInstance();
    private OrderService orderService = OrderService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order reqOrder = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Order orderInDb = (Order) context.get(ContextConstant.ORDER_IN_DB);
        OrderItem reqOrderItem = (OrderItem) context.get(ContextConstant.ORDER_ITEM);
        boolean customerRequest = orderService.isCustomerRequest(reqOrder);

        OrderItem sameOrderItem = (OrderItem) context.get(ContextConstant.SAME_ORDER_ITEM);
        if (sameOrderItem == null) {
            logger.info("not find the same order item");
            return false;
        }

        //如果找到的菜是B端添加的，或者是C端先付款模式并且用户已经确认了，那么需要处理库存
        //C端后付款模式后，已经发送到厨房了，不用再处理
        reqOrderItem.setId(sameOrderItem.getId());
        List<Stock> stockList = StockService.getInstance().waiterAddOneItem(orderInDb, reqOrderItem);

        if (isNotEmpty(stockList)) {
            logger.info("some item not available");
            context.put(ContextConstant.CONTEXT_RESULT, commonService.createResponse(stockList));
            return true;
        }

        //C端场景，有相同项，新增数量
        Integer addCount = getInt(reqOrderItem.getCount());
        sameOrderItem.setCount(getInt(sameOrderItem.getCount()) + addCount);
        //只要还能够添加，那么清空优惠券
        orderInDb.setTicketCodeList(new ArrayList<>());
        orderInDb.setParentTicketCodeList(new ArrayList<>());
        orderService.calPriceForB_C_END(orderInDb);
        commonService.save(orderInDb);

        //相同的orderItem写入日志
        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        OrderOpLog orderOpLog = orderOpLogService.content(orderInDb, ADD_ITEM);
        orderOpLogService.addOrderItemLog(sameOrderItem, orderOpLog);


        //最近返回的时候，判断是否需要过滤
        if (!customerRequest) {
            orderService.filterCheckoutOrderItem(orderInDb);
        }

        context.put(ContextConstant.CONTEXT_RESULT, commonService.createResponse(orderInDb));
        return true;
    }
}




